unit uGameClient;

interface

uses
  Sockets, uProtoBuf, uProtocol;

type
  TGameStep = (gsConnect, gsLogin, gsRun);

  TGameClient = class(TObject)
  private
    FHost: string;
    FPort: Integer;
    FGameStep: TGameStep;
    FLastRunTick: Cardinal;
    FSendKey: array[0..KEY_LEN-1] of byte;
    FRecvKey: array[0..KEY_LEN-1] of byte;
    FEncrypted: Boolean;
    FTcpClient: TTcpClient;
    procedure OnConnect(sender: TObject);
    procedure OnDisconnect(sender: TObject);
    procedure OnReceive(sender: TObject; buf: PAnsichar; var dataLen: Integer);
    function Encrypt(src: PByte; srcLen: Integer): Integer;
    function Decrypt(src: PByte; srcLen: Integer;
                     dest: PByte; destLen: Integer): Integer;    
    procedure LoginCmd;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Init(const host: string; port: Integer);
    procedure Run(tick: Cardinal);
    procedure SendProtoBuffer(pkg: TPackageOut; message: TProtoBufMessage=nil);
  end;
  
implementation

uses
  SysUtils, Windows,
  uGlobal;

{ TGameClient }

constructor TGameClient.Create;
begin
  inherited;
  FEncrypted := True;
  FGameStep := gsConnect;
  FLastRunTick := GetTickCount;
  FTcpClient := TTcpClient.Create(nil);
  FTcpClient.OnConnect := OnConnect;
  FTcpClient.OnDisconnect := OnDisconnect;
  FTcpClient.OnReceive := OnReceive; 
  Move(DEFAULT_KEY, FSendKey, Sizeof(DEFAULT_KEY));
  Move(DEFAULT_KEY, FRecvKey, Sizeof(DEFAULT_KEY));
end;

destructor TGameClient.Destroy;
begin
  FTcpClient.Free;
  inherited;
end;

procedure TGameClient.OnConnect(sender: TObject);
begin
  FGameStep := gsLogin;
  Move(DEFAULT_KEY, FSendKey, Sizeof(DEFAULT_KEY));
  Move(DEFAULT_KEY, FRecvKey, Sizeof(DEFAULT_KEY));  
  DebugLog('OnConnect');
end;

procedure TGameClient.OnDisconnect(sender: TObject);
begin
  FGameStep := gsConnect;
  DebugLog('OnDisconnect');
end;

procedure TGameClient.OnReceive(sender: TObject; buf: PAnsichar; var dataLen: Integer);
begin
  DebugLog('OnReceive');
end;

function TGameClient.Encrypt(src: PByte; srcLen: Integer): Integer;
var
  i: Integer;
  tmp: Byte;
begin
  result := -1;
  for i := 0 to srcLen - 1 do
  begin
    if i>0 then
    begin
      Dec(src); tmp := src^; Inc(src);
      FSendKey[i mod 8] := Powerex(FSendKey[i mod 8]+tmp, i);
      src^ := Powerex(src^, FSendKey[i mod 8]) + tmp;
    end else
      src^ := Powerex(src^, FSendKey[0]);
    Inc(Src);
  end;
end;

function TGameClient.Decrypt(src: PByte; srcLen: Integer;
  dest: PByte; destLen: Integer): Integer;
var
  i: Integer;
  tmp: Byte;
begin
  result := -1;
  for i := 0 to srcLen - 1 do
  begin
    if i>0 then
    begin
      Dec(src); tmp := src^; Inc(src);
      FRecvKey[i mod 8] := Powerex(FRecvKey[i mod 8]+tmp, i);
      dest^ := Powerex(src^-tmp, FRecvKey[i mod 8]);
    end else
      dest^ := Powerex(src^, FRecvKey[0]);
    Inc(Src);
    Inc(dest);
  end;
end;

procedure TGameClient.LoginCmd;
var
  i: Integer;
  tmpKey: array[0..KEY_LEN] of byte;
  Head: TPackageOut;
  Msg: TLoginReqMsg;
begin
  Head := TPackageOut.Create(U_LOGIN_GATEWAY);
  Msg := TLoginReqMsg.Create;
  Msg.Key := '5a5857a2-3502-4250-ab07-ff9f990e0c1d';
  for i := 0 to KEY_LEN -1 do
    tmpKey[i] := Ord('a');//Random(255);
  Move(tmpKey, Msg.EKey, KEY_LEN);
  SendProtoBuffer(Head, Msg);
  Move(tmpKey, FSendKey, KEY_LEN);
  Move(tmpKey, FRecvKey, KEY_LEN);
end;

procedure TGameClient.Init(const host: string; port: Integer);
begin
  FHost := host;
  FPort := port;
  FTcpClient.RemoteHost := FHost;
  FTcpClient.RemotePort := IntToStr(FPort);
  FTcpClient.Connect;
end;

procedure TGameClient.Run(tick: Cardinal);
begin
  if tick-FLastRunTick >1000 then
  begin
    FLastRunTick := tick;
    case FGameStep of
    gsConnect:
      FTcpClient.Connect;
    gsLogin:
      LoginCmd;
    end;
  end;
end;

procedure TGameClient.SendProtoBuffer(pkg: TPackageOut; message: TProtoBufMessage);
var
  wBuffer: TWritingBuffer;
  {$IFDEF DEBUG}
  deBuf: array[0..1024] of char;
  {$ENDIF}  
begin
  if Assigned(message) then
  begin
    wBuffer := TWritingBuffer.Create;
    message.WriteExternal(wBuffer);
    pkg.WriteBytes(wBuffer);
    message.Free;
    wBuffer.Free;
  end;
  pkg.pack;

  {$IFDEF DEBUG}
  FillChar(deBuf, Sizeof(deBuf), 0);
  Move(pkg.DataPtr^, debuf, pkg.Length);
  {$ENDIF}  
  if FEncrypted then
    Encrypt(pkg.DataPtr, pkg.Length);
  if FTcpClient.Connected then
  begin
    FTcpClient.SendBuf(pkg.DataPtr^, pkg.Length);
  end;
  pkg.Free;
  FGameStep := gsRun;
end;

end.
